﻿using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Devonline.Security;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using HashAlgorithm = Devonline.Core.HashAlgorithm;

namespace Devonline.AspNetCore.Security;

/// <summary>
/// 使用数据库上下文存储的数据安全服务
/// 证书和密钥的存储会固定使用 Certificate 和 Secret 相关的对象
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
public class UserDataSecurityService<TDbContext> :
    DataSecurityService<TDbContext>,
    IUserDataSecurityService<TDbContext>,
    IDataSecurityService,
    ISecurityService
    where TDbContext : BaseSecurityDbContext
{
    protected readonly HttpContext _httpContext;

    public UserDataSecurityService(
        ILogger<UserDataSecurityService<TDbContext>> logger,
        DataSecuritySetting securitySetting,
        IHttpContextAccessor httpContextAccessor,
        TDbContext context) : base(logger, securitySetting, context)
    {
        _httpContext = httpContextAccessor.HttpContext!;
        UserId = _httpContext.GetUserId()!;
        UserName = _httpContext.GetUserName();
    }

    /// <summary>
    /// 当前登录的用户编号
    /// </summary>
    public string UserId { get; }
    /// <summary>
    /// 当前登录的用户
    /// </summary>
    public string UserName { get; }

    /// <summary>
    /// 获取或创建最新的用户密钥
    /// </summary>
    /// <param name="keyIV">密钥和初始化向量</param>
    /// <param name="identityId">身份标识</param>
    /// <param name="identityType">身份类型</param>
    /// <returns></returns>
    protected override async Task<Secret> GetOrCreateSecretAsync(KeyIV keyIV)
    {
        // keyIV 不存在则从数据库查询
        var keyId = keyIV.Id.ToString();
        var secret = await _secrets.FirstOrDefaultAsync(x => x.Id == keyId);
        if (secret is not null)
        {
            return secret;
        }

        var certificate = await GetOrCreateCertificateAsync();
        var x509Certificate2 = GetX509Certificate2(certificate);
        var aesKey = await _certificateSecurityService.EncryptAsync(x509Certificate2, keyIV.Value);
        secret = new Secret
        {
            Id = keyIV.Id.ToString(),
            IdentityType = IdentityType.User,
            OwnerId = UserId,
            CertificateId = certificate.Id,
            CipherMode = _securitySetting.CipherMode,
            KeySize = _securitySetting.KeySize,
            HashAlgorithm = HashAlgorithm.SHA256,
            EncryptionAlgorithm = Core.SymmetricAlgorithm.AES_256_CBC,
            Start = DateTime.UtcNow,
            End = DateTime.UtcNow.AddMinutes(_securitySetting.ExpireTime),
            HashCode = Convert.ToBase64String(keyIV.Value),
            Content = Convert.ToBase64String(aesKey)
        };

        secret.Create(UserName);
        _secrets.Add(secret);
        await _dbContext.SaveChangesAsync();
        return secret;
    }
    /// <summary>
    /// 获取或创建最新的用户证书
    /// </summary>
    /// <param name="identityId">身份标识</param>
    /// <param name="identityType">身份类型</param>
    /// <returns></returns>
    protected override async Task<Certificate> GetOrCreateCertificateAsync()
    {
        var certificate = await _certificates.Where(x => x.IdentityType == IdentityType.User && x.OwnerId == UserId && x.End != null && x.End.Value > DateTime.UtcNow).OrderBy(x => x.Start).LastOrDefaultAsync();
        if (certificate is null)
        {
            //用户证书尚不存在时创建
            certificate = new Certificate
            {
                IdentityType = IdentityType.User,
                OwnerId = UserId,
                Start = DateTime.UtcNow,
                End = DateTime.UtcNow.AddMonths(_dataSecuritySetting.CertificateExpireTime),
                HashAlgorithm = HashAlgorithm.SHA256,
                EncryptionAlgorithm = Core.SymmetricAlgorithm.AES_256_CBC,
                SignatureAlgorithm = EncryptionAlgorithm.RSA,
                KeySize = _dataSecuritySetting.RSAKeySize,
            };

            certificate.Create(UserName);
            var fileName = UserName + CHAR_HLINE + certificate.Id + DEFAULT_EXCEL_FILE_EXTENSION_CERTIFICATE;
            certificate.Path = GetFilePath(fileName);
            var certificateInfo = GetDefaultCertificateInfo();
            certificateInfo.Password = certificate.Id.GetHashString();
            certificateInfo.FilePath = certificate.Path.GetAbsolutePath();
            var x509Certificate2 = await _certificateSecurityService.GenerateAsync(certificateInfo);
            certificate.Password = certificateInfo.Password;
            var publicKey = x509Certificate2.GetRSAPublicKey()!.ExportRSAPublicKey();
            certificate.PublicKey = Convert.ToBase64String(publicKey);
            certificate.HashCode = Convert.ToBase64String(SHA256.HashData(publicKey));
            certificate.Content = Convert.ToBase64String(x509Certificate2.RawData);
            await _dbContext.SaveChangesAsync();
        }

        return certificate;
    }
}